import json
import urllib
import re
import datetime
import math
from difflib import HtmlDiff
from django.db import IntegrityError
from django.db.models import Q
from django.core.urlresolvers import reverse
from django.core import serializers
from django.http import HttpResponse, HttpResponseRedirect, Http404
from django.shortcuts import render_to_response
from django.template import RequestContext
from django.conf import settings
from django.contrib.auth import logout
from djambe.wiki.models import Page, Tag, History
from djambe.wiki.forms import PageForm
from djambe.wiki.utils import generate_slug, word_split


def front_page(request):
  for p in Page.objects.all():
    if p.is_viewable(request.user):
      return HttpResponseRedirect(reverse(view, args=[p.id]))
  raise Http404


def view(request, arg):
  '''
  View for viewing wiki page by its ID or Title.
  '''

  t_str = urllib.unquote(arg)
  try:
    p = Page.objects.get(title=t_str)
  except Page.DoesNotExist:
    if re.match('^\d+$', arg):
      try:
        p = Page.objects.get(id=arg)
      except Page.DoesNotExist:
        raise Http404
    else:
      if request.user.is_authenticated():
        return new_form(request, t_str)
      else:
        raise Http404


  result = p.is_viewable(request.user)
  if result[0]:
    return render_to_response(
      'view.html',
      {
        "page": p,
        "page_title": p.title,
        "render": True,
      },
      context_instance=RequestContext(request)
    )
  elif result[1] == 1:
    # Not Allowed
    return render_to_response(
      'error.html',
      {
        "message": 'You are not allowed to see this contents.',
      },
      context_instance=RequestContext(request)
    )
  elif result[1] == 2:
    # Need Login
    return HttpResponseRedirect(reverse('login') + '?next=%s' % request.path)
  else:
    # Deleted
    return render_to_response(
      'error.html',
      {
        "message": 'This page is deleted.',
      },
      context_instance=RequestContext(request)
    )


def blank_form(request):
  if not request.user.is_authenticated():
    return HttpResponseRedirect(reverse('login') + '?next=%s' % request.path)
  else:
    return new_form(request, '')


def new_form(request, arg):
  if arg == '':
    form = PageForm()
  else:
    form = PageForm(
      {
        "title": arg,
        "text" : '*' + arg + "\n",
        "is_public": 'public',
        "is_editable": 'open',
      }
    )
  return render_to_response(
    'edit.html',
    {
      "instruction": 'New Page',
      "id": 0,
      "form": form,
      "page_title": 'New Page'
    },
    context_instance=RequestContext(request)
  )


def save(request, num):
  if not request.user.is_authenticated():
    return HttpResponseRedirect(reverse('login') + '?next=%s' % request.path)

  if num != 0:
    try:
      p = Page.objects.get(id=num)
      p.unlock(request.user)
      old_tags = list(p.tags.all())
      form = PageForm(request.POST, instance=p)
    except Page.DoesNotExist:
      num = 0
      form = PageForm(request.POST)
      old_tags = []
  else:
    form = PageForm(request.POST)
    old_tags = []

  if form.is_valid():
    new_page = form.save(commit=False)
    new_page.author = request.user
    new_page.save()
    form.save_m2m()
    history = History(
      page = new_page,
      title = new_page.title,
      text = new_page.text,
      updated = new_page.updated,
      author = new_page.author,
      action = 'save',
      is_public = new_page.is_public,
    )
    history.save()
    new_tags = [form.cleaned_data['new_tag1'], form.cleaned_data['new_tag2'], form.cleaned_data['new_tag3']]
  else:
    e = form.errors
    if num != 0:
      instruction = 'Edit Page'
      page_title = instruction + ': ' + p.title
    else:
      instruction = page_title = 'New Page'

    return render_to_response(
      'edit.html',
      {
        "errors": e,
        "instruction": instruction,
        "id": num,
        "form": form,
        "page_title": page_title,
      },
      context_instance=RequestContext(request)
    )

  # Create new tags if needed
  for new_tag in new_tags:
    if '' != new_tag:
      try:
        t = Tag.objects.create(text=new_tag)
      except IntegrityError:
        t = Tag.objects.get(text=new_tag)
      new_page.tags.add(t)

  # Update tag's page_count
  new_page.update_tag_count(old_tags, True)
#  updated_tags = list(new_page.tags.all())
#  if num != 0:
#    for tag in old_tags:
#      if not tag in updated_tags:
#        updated_tags.append(tag)
#  for tag in updated_tags:
#    tag.page_count()
#    if tag.count == 0:
#      tag.delete()
#    else:
#      tag.save()

  # Redirect to the view page
  return HttpResponseRedirect(reverse(view, args=[new_page.id]))


def ajax_save(request, num):
  '''
  Save without page transfer
  '''

  if not request.user.is_authenticated():
    result = {
      'id': num,
      'message': '<span style="color:darkred">Authentication failed.</span>'
    }
    return HttpResponse(
      json.dumps(result)
    )

  if num != 0:
    try:
      p = Page.objects.get(id=num)
      p.lock(request.user)
      old_tags = list(p.tags.all())
      form = PageForm(request.POST, instance=p)
    except Page.DoesNotExist:
      num = 0
      form = PageForm(request.POST)
      old_tags = []
  else:
    form = PageForm(request.POST)
    old_tags = []

  if form.is_valid():
    new_page = form.save(commit=False)
    new_page.author = request.user
    new_page.save()
    new_page.tags = []
    new_tags = [form.cleaned_data['new_tag1'], form.cleaned_data['new_tag2'], form.cleaned_data['new_tag3']]
    if form.cleaned_data['tag_text'] != '':
      for tag in form.cleaned_data['tag_text'].split(','):
        try:
          t = Tag.objects.get(id=tag)
          new_page.tags.add(t)
        except Tag.DoesNotExist:
          pass

    # Create new tags if needed
    for new_tag in new_tags:
      if '' != new_tag:
        try:
          t = Tag.objects.create(text=new_tag)
        except IntegrityError:
          t = Tag.objects.get(text=new_tag)
        new_page.tags.add(t)

    # Update tag's page_count
    new_page.update_tag_count(old_tags)
#    updated_tags = list(new_page.tags.all())
#    if num != 0:
#      for tag in old_tags:
#        if not tag in updated_tags:
#          updated_tags.append(tag)
#    for tag in updated_tags:
#      tag.page_count()
#      if tag.count == 0:
#        tag.delete()
#      else:
#        tag.save()

    id = new_page.id
    message = 'Saved'

  # When form is invalid
  else:
    id = num
    message = '<span style="color:darkred">' + form.errors.keys()[0] + ': ' + form.errors.values()[0][0] + '</span>'

  result = {
    'id': id,
    'message': message
  }
  return HttpResponse(
    json.dumps(result)
  )


def ajax_unlock(request, num):

  if not request.user.is_authenticated():
    result = {
      'result': 'Fail'
    }
    return HttpResponse(
      json.dumps(result)
    )

  try:
    p = Page.objects.get(id=num)
    p.unlock(request.user)
  except:
    result = {
      'result': 'Fail'
    }
    return HttpResponse(
      json.dumps(result)
    )

  result = {
    'result': 'Success'
  }
  return HttpResponse(
    json.dumps(result)
  )


def edit_form(request, num):
  if not request.user.is_authenticated():
    return HttpResponseRedirect(reverse('login') + '?next=%s' % request.path)

  p = Page.objects.get(id=num)
  result = p.is_protected(request.user)
  if not result[0]:
    # Editable
    form = PageForm(instance=p)
    p.lock(request.user)

    return render_to_response(
      'edit.html',
      {
        "errors": result[1],
        "instruction": 'Edit Page',
        "id": num,
        "form": form,
        "page_title": 'Edit Page' + ': ' + p.title
      },
      context_instance=RequestContext(request)
    )
  else:
    # Not editable
    return render_to_response(
      'error.html',
      {
        "message": result[1],
        "id": num,
        "title": p.title,
      },
      context_instance=RequestContext(request)
    )


def quit_editing(request, num):
  if not request.user.is_authenticated():
    return HttpResponseRedirect(reverse('login') + '?next=%s' % request.path)

  try:
    p = Page.objects.get(id=num)
    p.unlock(request.user)
    return HttpResponseRedirect(reverse(view, args=[num]))
  except Page.DoesNotExist:
    return front_page(request)


def page_list(request):
  if not request.user.is_authenticated():
    page_list = Page.objects.filter(is_deleted=False, is_public='public').order_by('-updated')
  else:
    page_list = Page.objects.filter(Q(is_deleted=False) & (Q(is_public='public') | Q(is_public='internal') | Q(author=request.user))).order_by('-updated')

  return render_to_response(
    'page_list.html',
    {
      "header": 'All Pages',
      "page_list": page_list,
      "page_title": 'All Pages',
    },
    context_instance=RequestContext(request)
  )


def logout_view(request):
  logout(request)
  return HttpResponseRedirect(request.GET['next'])


def delete_confirm(request, num):
  if not request.user.is_authenticated():
    return HttpResponseRedirect(reverse('login') + '?next=%s' % request.path)

  try:
    p = Page.objects.get(id=num)
    p.unlock(request.user)
  except Page.DoesNotExist:
    raise Http404
  return render_to_response(
      'delete_confirm.html',
      {
        "page": p
      },
      context_instance=RequestContext(request)
    )


def delete(request, num):
  if not request.user.is_authenticated():
    return HttpResponseRedirect(reverse('login') + '?next=%s' % request.path)

  try:
    p = Page.objects.get(id=num)
  except Page.DoesNotExist:
    raise Http404
  p.delete()
  history = History(
    page = p,
    title = p.title,
    text = p.text,
    updated = datetime.datetime.today(),
    author = request.user,
    action = 'delete',
    is_public = p.is_public,
  )
  history.save()
  return HttpResponseRedirect(reverse('root'))


def version_list(request, num):
  try:
    p = Page.objects.get(id=num)
  except Page.DoesNotExist:
    raise Http404
  version_list = History.objects.filter(page=p).order_by('-id')

  return render_to_response(
    'version_list.html',
    {
      "version_list": version_list,
      "page_title": 'History of ' + p.title,
    },
    context_instance=RequestContext(request)
  )


def view_version(request, num):
  try:
    version = History.objects.get(id=num)
  except History.DoesNotExist:
    raise Http404

  return render_to_response(
    'view_version.html',
    {
      "version": version,
      "page_title": version.title + ' - Updated: ' + version.updated.strftime("%Y-%m-%d %H:%M"),
      "render": True,
#      "diff": diff,
      "page": version.page,
    },
    context_instance=RequestContext(request)
  )


def revert_version(request, num):
  try:
    version = History.objects.get(id=num)
  except History.DoesNotExist:
    raise Http404

  version.page.title = version.title
  version.page.text = version.text
  version.page.author = version.author
  version.page.is_deleted = False
  version.page.save()

  history = History(
    page = version.page,
    title = version.title,
    text = version.text,
    updated = datetime.datetime.today(),
    author = request.user,
    action = 'revert',
    is_public = version.is_public,
  )
  history.save()

  return HttpResponseRedirect(reverse(view, args=[version.page.id]))


def tag(request, num):
  try:
    tag = Tag.objects.get(id=num)
  except Tag.DoesNotExist:
    raise Http404

  page_list_by_tag = Page.objects.filter(tags__in=[tag]).distinct().order_by('-updated')

  return render_to_response(
    'page_list.html',
    {
      "header": 'All Pages with Tag ' + tag.text,
      "page_list": page_list_by_tag,
      "page_title": 'All Pages with Tag ' + tag.text,
    },
    context_instance=RequestContext(request)
  )


def history(request):
  if not request.user.is_authenticated():
    version_list = History.objects.filter(is_public='public').order_by('-updated')
  else:
    version_list = History.objects.filter(Q(is_public='public') | Q(is_public='internal') | Q(author=request.user)).order_by('-updated')
  return render_to_response(
    'version_list.html',
    {
      "version_list": version_list,
      "page_title": 'History',
    },
    context_instance=RequestContext(request)
  )


def iframe_view(request, arg, iframe_id):
  t_str = urllib.unquote(arg)
  try:
    p = Page.objects.get(title=t_str)
  except Page.DoesNotExist:
    return HttpResponse("Not found.")

  result = p.is_viewable(request.user)
  if result[0]:
    return render_to_response(
      'iframe_view.html',
      {
        "page": p,
        "iframe_id": iframe_id,
      },
      context_instance=RequestContext(request)
    )
  elif result[1] == 1 or result[1] == 2:
    return HttpResponse("You are not allowed to see this contents.")
  else:
    return HttpResponse("This contents is deleted.")


def print_view(request, num):
  '''
  Printer friendly view by its ID.
  '''

  try:
    p = Page.objects.get(id=num)
  except Page.DoesNotExist:
    raise Http404

  result = p.is_viewable(request.user)
  if result[0]:
    return render_to_response(
      'print_view.html',
      {
        "page": p,
        "page_title": p.title,
      },
      context_instance=RequestContext(request)
    )
  elif result[1] == 1:
    # Not Allowed
    return render_to_response(
      'error.html',
      {
        "message": 'You are not allowed to see this contents.',
      },
      context_instance=RequestContext(request)
    )
  elif result[1] == 2:
    # Need Login
    return HttpResponseRedirect(reverse('login') + '?next=%s' % request.path)
  else:
    # Deleted
    return render_to_response(
      'error.html',
      {
        "message": 'This page is deleted.',
      },
      context_instance=RequestContext(request)
    )


def search(request):
  p = re.compile('[\.\^\$\*\+\?\{\[\\\|\(\)]')
  st = p.sub('', request.GET['search_text'])
  if not request.user.is_authenticated():
    pages = Page.objects.filter(is_deleted=False, is_public='public').order_by('-updated')
  else:
    pages = Page.objects.filter(Q(is_deleted=False) & (Q(is_public='public') | Q(is_public='internal') | Q(author=request.user))).order_by('-updated')

  word_list = []
  words = word_split(st)
  if len(words) == 0:
    return page_list(request)
  c = 0
  for w in words:
    if w == '':
      continue
    w = w[0:settings.SEARCH_MAX_LEN]
    pages = pages.filter(Q(text__icontains=w) | Q(title__icontains=w))
    word_list.append(w)
    c += 1
    if c >= settings.SEARCH_MAX_WORDS:
      break

  regex_list = []
  for w in words:
    regex_list.append(re.compile(w , re.IGNORECASE))

  page_list_work = []
  today = datetime.datetime.today()
  for p in pages:
    d = today - p.updated
    if d.days > 125:
      rate = 4
    else:
      rate = math.log(d.days + 1, 5) + 1
    c = 0
    for r in regex_list:
      c = c + len(r.split(p.text)) + 2 * (len(r.split(p.title)) - 1)
    page_list_work.append((c / (rate * len(p.text)), p))
#    page_list_work.append((c, p))
  page_list_work.sort(reverse=True)

  sorted_page_list = []
  for p in page_list_work:
    sorted_page_list.append(p[1])

  return render_to_response(
    'search_result.html',
    {
      "word_list": word_list,
      "regex_list": regex_list,
      "page_list": sorted_page_list,
      "page_title": 'Search Results for ' + ' '.join(word_list),
    },
    context_instance=RequestContext(request)
  )
